#include "MainPage.h"
#include <unistd.h>
#include "curl/curl.h"
#include <fstream>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <map>
#include "cJSON.h"

#define CONFIG_FILE "./tmp/sysconfig"

#define INSTALL_WIRELESS_CMD_1 "modprobe mac80211.ko"
#define INSTALL_WIRELESS_CMD_2 "modprobe rtl8xxxu.ko"
#define WLAN_UP_CMD "ifconfig wlan0 up"
#define WPA_SERVER_CMD "wpa_supplicant -Dwext -c /etc/wpa_supplicant.conf -i wlan0 -B"
#define WPA_UDHCCP "udhcpc -b -i wlan0 -q"

#define WEATHER_NOW_API "https://api.seniverse.com/v3/weather/now.json?key=%s&location=ip&language=zh-Hans&unit=c"
#define WEATHER_DAILY_API "https://api.seniverse.com/v3/weather/daily.json?key=%s&location=ip&language=zh-Hans&unit=c&start=0&days=1"

static int main_thread_func(MainPage *obj);
static int boot_thread_func(MainPage *obj);

static string curl_http_get(const char *url);
static ssize_t cul_write_data_to_string(void *buf, size_t size, size_t nmemb, void *arg);
static void set_system_date(gui_time_t &time);
static char **string_to_argv(const char *exec, string &str);
static void main_page_run_application(const char *exec, char *const argv[]);

static gui_opt_t gui_opts;         // UI回调函数集合
static MainPage::sysconfig config; // 配置信息

static const char *config_data_header[] =
    {
        "brightness:",
        "volume:",
        "auto_position:",
        "mainbg:",
        "host_ip:",
        "weather_key:",
        "weather_city:",
};

static const char *app_info_header[] =
    {
        "name=",
        "exec=",
        "argv=",
        "icon="};

static const char *weather_items[] =
    {
        "text",
        "wind_direction",
        "code",
        "temperature",
        "feels_like",
        "pressure",
        "humidity",
        "wind_scale",
};

/*
 *@brief MainPage构造函数
 *@param exit_cb 退出进程回调函数
 */
MainPage::MainPage(exit_cb_t exit_cb, mutex &mutex)
{
  thread_exit_flag = false;

  gui_opts.exit_cb = exit_cb;
  gui_opts.run_app = main_page_run_application;

  tick = 0;
  ui_mutex = &mutex;

  curl_global_init(CURL_GLOBAL_ALL); // CURL全局初始化

  pthread = new thread(main_thread_func, this); // 创建主UI执行线程
}

MainPage::~MainPage()
{
  thread_exit_flag = true;
  pthread->join();
  delete pthread;

  curl_global_cleanup(); // CURL全局释放

  main_page_gui_release();
}

/*
 *@brief 读取设置信息
 *@param config 保存设置信息
 *@return true - 读取到了配置信息  false - 缺省值配置信息
 */
bool MainPage::readconfig(sysconfig &config)
{
  ifstream file;

  file.open(CONFIG_FILE, ios::in);

  if (file.is_open() != true)
  {
    config.brightness = 50; // 缺省值
    config.volume = 50;
    config.auto_position = 0;
    config.mainbg_file = "mainbg_1.bin";
    config.host_ip = "127.0.0.1";
    config.weather_key = "";
    config.weather_city = "北京";

    appinfo info = {.name = "设置", .exec = "setting", .argv = "\'null\'", .icon = "setting_icon.bin"};
    config.apps.push_back(info);

    return false;
  }

  char *buf = new char[4096];
  memset(buf, 0, 4096);
  file.read(buf, 4096);
  file.close();

  string config_string(buf);
  delete[] buf;

  size_t dataStart = 0;
  size_t dataEnd = 0;
  string dataStr;

  int *value[] = {&config.brightness, &config.volume, &config.auto_position};
  string *confi_str[] = {&config.mainbg_file, &config.host_ip,
                         &config.weather_key, &config.weather_city};
  // 读取系统配置
  for (int i = 0; i < 7; i++)
  {
    dataStart = config_string.find(config_data_header[i], dataEnd) + strlen(config_data_header[i]); // 寻找前导字符串
    dataEnd = config_string.find('\n', dataStart);                                                  // 寻找换行符
    if (dataEnd != string::npos)
    {
      dataStr = config_string.substr(dataStart, dataEnd - dataStart);
      if (i >= sizeof(value) / sizeof(value[0]))
        *(confi_str[i - sizeof(value) / sizeof(value[0])]) = dataStr;
      else
        *(value[i]) = stoi(dataStr);
    }
  }

  // 读取预安装应用程序信息
  dataStart = config_string.find("app", dataEnd); // 寻找app字符串
  do
  {
    appinfo info;
    string *app_str[] = {&info.name, &info.exec, &info.argv, &info.icon};

    for (int i = 0; i < sizeof(app_str) / sizeof(app_str[0]); i++)
    {
      if (dataStart != string::npos)
      {
        dataStart = config_string.find(app_info_header[i], dataEnd); // 寻找前导字符串
        if (dataStart != string::npos)
        {
          dataStart += strlen(app_info_header[i]);       // 跳过前导字符串
          dataEnd = config_string.find('\n', dataStart); // 寻找换行符
          if (dataEnd != string::npos)
            *(app_str[i]) = config_string.substr(dataStart, dataEnd - dataStart);
        }
      }
    }

    config.apps.push_back(info);                    // 向容器插入一个元素
    dataStart = config_string.find("app", dataEnd); // 寻找下一个app字符串
  } while (dataStart != string::npos);

  return true;
}

/*
 *@brief 保存设置信息
 *@param config 保存的设置信息
 */
void MainPage::saveconfig(sysconfig &config)
{
  string s = "brightness:" + to_string(config.brightness) + "\n";

  s += "volume:" + to_string(config.volume) + "\n";
  s += "auto_position:" + to_string(config.auto_position) + "\n";
  s += "mainbg:" + config.mainbg_file + "\n";
  s += "host_ip:" + config.host_ip + "\n";
  s += "weather_key:" + config.weather_key + "\n";
  s += "weather_city:" + config.weather_city + "\n";

  vector<appinfo>::iterator it;

  for (it = config.apps.begin(); it != config.apps.end(); it++)
  {
    string app_info = "\napp={\n\tname=" + it->name + "\n\texec=" + it->exec + "\n\targv=" + it->argv + "\n\ticon=" + it->icon + "\n}";
    s += app_info;
  }

  ofstream file;

  file.open(CONFIG_FILE, ios::out); // 写方式打开文件

  file << s << endl;

  file.close();
}

/*
 *@brief 获取天气数据
 *@return 成功 - true  失败 - false
 */
bool MainPage::getWeather(gui_weather_t &weather)
{
  bool ret = false;
  char *api = new char[256];
  sprintf(api, WEATHER_NOW_API, config.weather_key.c_str());

  string str = curl_http_get(api); // 获取实况天气

  cJSON *cjson = cJSON_Parse(str.c_str()); // 打包cJSON数据格式
  if (cjson != NULL)
  {
    cJSON *result = cJSON_GetObjectItem(cjson, "results");
    cJSON *result_0 = cJSON_GetArrayItem(result, 0);
    cJSON *now = cJSON_GetObjectItem(result_0, "now");
    cJSON *location = cJSON_GetObjectItem(result_0, "location");

    cJSON *position = cJSON_GetObjectItem(location, "name");
    if (position != NULL)
      strcpy(weather.position, position->valuestring);
    else
      strcpy(weather.position, "定位失败");

    char *dataString[] = {weather.text, weather.wind_dir};
    int *dataValue[] = {&weather.code, &weather.temp, &weather.feel_temp,
                        &weather.pressure, &weather.humi, &weather.wind_level};

    for (int i = 0; i < 8; i++)
    {
      if (now != NULL)
      {
        cJSON *data = cJSON_GetObjectItem(now, weather_items[i]);
        if (data == NULL)
          continue;

        if (i < 2)
          strcpy(dataString[i], data->valuestring);
        else
          *(dataValue[i - 2]) = atoi(data->valuestring);
      }
    }

    cJSON_Delete(cjson);
  }

  sprintf(api, WEATHER_DAILY_API, config.weather_key.c_str());
  str = curl_http_get(api);         // 获取今日天气
  cjson = cJSON_Parse(str.c_str()); // 打包cJSON数据格式
  if (cjson != NULL)
  {
    cJSON *result = cJSON_GetObjectItem(cjson, "results");
    cJSON *result_0 = cJSON_GetArrayItem(result, 0);
    cJSON *daily = cJSON_GetObjectItem(result_0, "daily");
    cJSON *today = cJSON_GetArrayItem(daily, 0);

    cJSON *data = cJSON_GetObjectItem(today, "high"); // 最高气温
    if (data != NULL)
      weather.max_temp = atoi(data->valuestring);

    data = cJSON_GetObjectItem(today, "low"); // 最低气温
    if (data != NULL)
      weather.min_temp = atoi(data->valuestring);

    cJSON_Delete(cjson);

    ret = true;
  }

  delete[] api;

  return ret;
}

/*
 *@brief 获取系统时间
 *@return 返回时间数据
 */
void MainPage::getSysTime(gui_time_t &sys_time)
{
  time_t seconds = time(NULL);

  struct tm *pstTIme = localtime(&seconds);
  sys_time.year = pstTIme->tm_year + 1900;
  sys_time.month = pstTIme->tm_mon + 1;
  sys_time.day = pstTIme->tm_mday;
  sys_time.hour = pstTIme->tm_hour;
  sys_time.min = pstTIme->tm_min;
  sys_time.sec = pstTIme->tm_sec;
  sys_time.week = pstTIme->tm_wday;
}

/*
 *@brief 安装应用程序
 *@param apps 应用程序表
 */
void MainPage::installApplications(vector<appinfo> &apps)
{
  vector<appinfo>::iterator it;

  for (it = apps.begin(); it != apps.end(); it++)
  {
    int exec_len = it->exec.length();
    int icon_len = it->icon.length();

    char *exec = new char[exec_len + 3];
    char *icon = new char[icon_len + 14];
    const char *name = it->name.c_str();
    char **argv;

    sprintf(exec, "./%s", it->exec.c_str());
    sprintf(icon, "S:./res/icon/%s", it->icon.c_str());
    argv = string_to_argv(exec, it->argv);

    main_page_gui_add_application((name), exec, argv, icon); // 添加应用程序到UI

    delete[] icon;
    delete[] exec;
  }
}

/*
 *@brief 主UI线程
 *@param obj 主界面对象
 */
static int main_thread_func(MainPage *obj)
{
  usleep(50000);
  gui_time_t time;
  gui_weather_t weather;
  bool is_get_time_succeed = false;
  bool is_get_weather_succeed = false;

  // 启动操作
  thread boot_thread(boot_thread_func, obj); // 创建启动线程
  boot_thread.join();                        // 等待启动线程返回

  printf("boot thread return\n");

  usleep(50000);

  printf("main running\n");

  // obj->getWeather(weather);

  // string pos = obj->getPosition();
  // printf("地址: %s\n", pos.c_str());

  while (!obj->getExitFlag())
  {
    int tick = obj->getTick();

    if (tick % 5 == 0)
    {
      obj->getSysTime(time); // 获取系统时间(500ms)
      obj->getUIMutex()->lock();
      main_page_gui_update_date(&time); // 更新时间显示
      obj->getUIMutex()->unlock();
    }

    if (tick == 1 || is_get_weather_succeed == false) // 一小时更新一次天气，第一次失败则一直尝试直到成功
    {
      is_get_weather_succeed = obj->getWeather(weather);
      obj->getUIMutex()->lock();
      main_page_gui_update_weather(&weather); // 更新天气显示
      obj->getUIMutex()->unlock();
    }

    obj->tickInc();
    if (tick >= 36000) // 1小时
      obj->tickClear();

    usleep(100000); // 1 tick = 100ms
  }

  return 0;
}

/*
 *@brief 启动UI线程
 */
static int boot_thread_func(MainPage *obj)
{
  obj->getUIMutex()->lock();
  main_page_gui_boot_init(); // 创建启动界面UI,创建于top layer上覆盖主界面UI
  obj->getUIMutex()->unlock();
  usleep(50000);

  // 加载配置文件
  obj->getUIMutex()->lock();
  main_page_gui_boot_update("正在加载配置文件...");
  obj->getUIMutex()->unlock();
  if(obj->readconfig(config) != true)            // 读取配置文件
    obj->saveconfig(config);            // 写入缺省信息到配置文件
  obj->setBgFile(config.mainbg_file); // 设置背景图片


  // 加载网卡驱动
  obj->getUIMutex()->lock();
  main_page_gui_boot_update("正在加载网卡驱动...");
  obj->getUIMutex()->unlock();
  system(INSTALL_WIRELESS_CMD_1);
  sleep(1);
  system(INSTALL_WIRELESS_CMD_2);
  sleep(1);

  // 唤醒网卡
  obj->getUIMutex()->lock();
  main_page_gui_boot_update("正在启动网卡...");
  obj->getUIMutex()->unlock();
  system(WLAN_UP_CMD);
  sleep(1);

  // 启动wpa_supplicant服务
  obj->getUIMutex()->lock();
  main_page_gui_boot_update("正在启动无线网络服务...");
  obj->getUIMutex()->unlock();
  system(WPA_SERVER_CMD);
  sleep(3);
  system(WPA_UDHCCP);       //获取IP

  // 添加应用程序
  obj->getUIMutex()->lock();
  main_page_gui_boot_update("正在安装应用程序...");
  obj->getUIMutex()->unlock();
  sleep(1);
  
  obj->getUIMutex()->lock();
  main_page_gui_init(&gui_opts, obj->getBgFile().c_str()); // 初始化主界面UI
  obj->installApplications(config.apps);
  obj->getUIMutex()->unlock();

  obj->getUIMutex()->lock();
  main_page_gui_boot_update("欢迎使用");
  obj->getUIMutex()->unlock();
  sleep(1);

  obj->getUIMutex()->lock();
  main_page_gui_boot_release(); // 释放启动界面UI
  obj->getUIMutex()->unlock();

  return 0;
}

/*
 *@brief curl 发送http/https GET请求
 *@param url 请求的url
 *@return 返回接收的数据量,失败则返回""
 */
static string curl_http_get(const char *url)
{
  CURL *curl;
  CURLcode res;
  string str = "";

  curl = curl_easy_init(); // 初始化CURL
  if (curl != NULL)
  {
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");                    // 设置GET请求
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);                       // 不验证本地证书
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);                       // 不要验证主机证书
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cul_write_data_to_string); // 设置接收回调函数
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&str);                 // 设置接收回调函数参数
    res = curl_easy_perform(curl);                                           // 执行请求
    if (res != CURLE_OK)
      str = "";
    curl_easy_cleanup(curl);
  }

  return str;
}

/*
 *@brief 将获取到的数据写入string
 *@param buf 待接收的数据
 *@param size 接收数据包个数
 *@param nmemb 数据包大小
 *@arg 使用CURLOPT_WRITEDATA设置的string地址
 *@return 返回接收的数据量
 */
static ssize_t cul_write_data_to_string(void *buf, size_t size, size_t nmemb, void *arg)
{
  int len = size * nmemb;
  string *str = static_cast<string *>(arg);

  str->append((char *)buf, len);

  return len;
}

/*
 *@brief 将字符串参数转为 char**
 *@return 带有应用程序执行路径的完整argv
 *@最大支持5个参数
 */
static char **string_to_argv(const char *exec, string &str)
{
  char **argv;
  int i = 0;
  size_t dataStart = 0;
  size_t dataEnd = 0;
  string dataStr = "";

  argv = new char *[5];

  int len = strlen(exec) + 1;
  argv[i] = new char[len];
  sprintf(argv[i++], "%s", exec);

  do
  {
    dataStart = str.find('\'', dataEnd); // 寻找 ' 字符
    if (dataStart != string::npos)
    {
      dataStart += 1;
      dataEnd = str.find('\'', dataStart); // 寻找 '
      if (dataEnd != string::npos)
      {
        dataStr = str.substr(dataStart, dataEnd - dataStart);
        if (dataStr != "null")
        {
          int len = dataStr.length() + 1;
          argv[i] = new char[len];
          sprintf(argv[i], "%s", dataStr.c_str());
        }
        else
        {
          argv[i] = NULL;
          break;
        }
      }
    }
  } while (++i < 5);

  return argv;
}

/*
 *@brief 运行app回调函数
 *@brief exec app执行文件
 ^@argv app的main函数参数
 *@note 由于回调函数被UI线程(主线程)执行，因此会阻塞UI线程
 */
static void main_page_run_application(const char *exec, char *const argv[])
{
  if (exec == NULL)
    return;

  pid_t pid = fork(); // 创建子进程

  if (pid == 0) // 子进程
  {
    int ret = execv(exec, argv);
    if (ret < 0)
    {
      printf("create %s failed\n", exec);
      exit(0); // 子进程退出
    }
  }

  wait(NULL); // 阻塞等待子进程返回
}